import { BusinessException } from '@libs/common/exceptions';
import { redis_dd_token_key, RedisService } from '@libs/common/redis';
import { isDev } from '@libs/common/utils';
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { chunk } from 'lodash';
import pLimit from 'p-limit';

import {
  ApiDDAttendanceList,
  ApiDDGetColumnVal,
  ApiDDGetUserInfo,
  type AttendanceListParams,
} from './dingding.api';
import { DingAuthService } from './sdk/auth.service';
import { DingCardService } from './sdk/card.service';
import { DingMiService } from './sdk/im.service';
import { DingWorkflowService } from './sdk/workflow.service';

const DD_TOKEN_KEY = redis_dd_token_key();

@Injectable()
export class DingdingService {
  constructor() {}

  @Inject(RedisService) private redisService: RedisService;

  Auth: DingAuthService = new DingAuthService();

  Workflow: DingWorkflowService = new DingWorkflowService();

  Card: DingCardService = new DingCardService();

  Mi: DingMiService = new DingMiService();

  async getToken() {
    try {
      let token = await this.redisService.get<string>(DD_TOKEN_KEY);

      if (!token) {
        const data = await this.Auth.getAccessToken();
        const { accessToken, expireIn } = data;

        this.redisService.set(DD_TOKEN_KEY, accessToken, expireIn - 60);

        token = accessToken;
      }
      console.log('🚀 ~ DingdingService ~ getToken ~ token:', token);
      return token;
    } catch (error) {
      throw new BusinessException(error);
    }
  }

  async getUserInfo(code: string) {
    if (!code) throw new BusinessException('code is not exist');

    if (isDev) {
      return {
        device_id: '206cbb226495bb545f905b16bfd04379',
        name: '许帅领',
        sys: true,
        sys_level: 2,
        unionid: 'jXx2NlxDmB4yIQWQvUn3WgiEiE',
        userid: '104619303835158233',
      };
    }

    const token = await this.getToken();
    if (!token) throw new UnauthorizedException('dingding token is not exist');

    try {
      const data = await ApiDDGetUserInfo(token, code);

      return data;
    } catch (error) {
      throw error;
    }
  }

  async AttendanceList(body: Omit<AttendanceListParams, 'access_token'>) {
    const token = await this.getToken();
    if (!token) throw new UnauthorizedException('dingding token is not exist');

    const idGroup = chunk(body.userIdList, 50);
    const limit = pLimit(10);

    const input = idGroup.map((ids) => {
      return limit(() => {
        return ApiDDAttendanceList({
          offset: 0,
          access_token: token,
          workDateFrom: body.workDateFrom,
          workDateTo: body.workDateTo,
          userIdList: ids,
        });
      });
    });

    const result = await Promise.all(input);

    const results = result.flat();

    return results;
  }

  async getColumnVal(
    dto: Omit<Parameters<typeof ApiDDGetColumnVal>[0], 'access_token'>,
  ) {
    const { column_id_list, userid, from_date, to_date } = dto;

    const token = await this.getToken();
    if (!token) throw new UnauthorizedException('dingding token is not exist');

    const data = await ApiDDGetColumnVal({
      access_token: token,
      column_id_list,
      userid: userid,
      from_date,
      to_date,
    });

    return data;
  }
}
